/* Copyright 2001,2002,2003 NAH6 BV
 * All Rights Reserved
 *
 *  $Header: /var/lib/cvs/secphone/ui/misc/stringutils.cpp,v 1.6 2003/11/21 16:12:58 itsme Exp $
 *
 * various std::string utility functions
 *
 * oa. used to convert between wstring and string, dump data, parse strings
 *
 */

#include <windows.h>

#include "stringutils.h"

#include "debug.h"

#include <string>
#include <algorithm>

std::string ToString(WCHAR* wbuf)
{
    char *buf= new char[wcslen(wbuf)+1];

    _snprintf(buf, wcslen(wbuf), "%ls", wbuf);

    std::string str(buf, buf+wcslen(wbuf));
    delete buf;

    return str;
}
std::wstring ToWString(WCHAR* wbuf)
{
    return std::wstring(wbuf);
}
std::string ToString(char* buf)
{
    return std::string(buf);
}
std::wstring ToWString(char* buf)
{
    WCHAR *wbuf= new WCHAR[strlen(buf)+1];

    _snwprintf(wbuf, strlen(buf), L"%hs", buf);

    std::wstring wstr(wbuf, wbuf+strlen(buf));
    delete wbuf;

    return wstr;
}
std::string ToString(const std::wstring& wstr)
{
    return ToString((WCHAR*)wstr.c_str());
}
std::wstring ToWString(const std::string& str)
{
    return ToWString((char*)str.c_str());
}

// removes cr, lf, whitespace from end of string
void chomp(char *str)
{
    char *p= str+strlen(str)-1;

    while (p>=str && isspace(*p))
    {
        *p--= 0;
    }
}
void chomp(std::string& str)
{
    // !! i must be signed, otherwise loop will not terminate for an empty string
    for (int i=str.size()-1 ; i>=0 && isspace(str.at(i)) ; --i)
    {
        str.erase(i);
    }
}

// splits a list of blank separated optionally quoted parameters
// in a list of strings
bool SplitString(const std::string& str, StringList& strlist, bool bWithEscape/*= true*/)
{
    std::string::const_iterator pos= str.begin();
    bool bQuoted= false;
    bool bEscaped= false;
    std::string current;

    while (pos != str.end())
    {
        if (bEscaped)
        {
            current += *pos++;
            bEscaped= false;
        }
        else if (bQuoted)
        {
            switch(*pos)
            {
            case '"':
                bQuoted= false;
                strlist.push_back(std::string(current));
                //debug("added %hs\n", current.c_str());
                current.clear();
                ++pos;
                break;
            case '\\':
                if (bWithEscape)
                {
                    bEscaped= true;
                    ++pos;  // skip escaped char
                }
                // else fall through
            default:
                current += *pos++;
            }
        }
        else    // not escaped, and not quoted
        {
            switch(*pos)
            {
            case ' ':
            case '\t':
                ++pos;
                if (!current.empty())
                {
                    strlist.push_back(std::string(current));
                    //debug("added %hs\n", current.c_str());
                    current.clear();
                }
                break;

            case '"':
                bQuoted=true;
                ++pos;
                break;
            case '\\':
                if (bWithEscape) {
                    bEscaped= true;
                    ++pos;  // skip escaped char
                    break;
                }
                // else fall through
            default:
                current += *pos++;
            }
        }
    }
    if (!current.empty())
    {
        strlist.push_back(std::string(current));
        //debug("added %hs\n", current.c_str());
        current.clear();
    }
    if (bQuoted || bEscaped)
    {
        debug("ERROR: Unterminated commandline\n");
        return false;
    }

    return true;
}

// joins strings from a list to form a single string
std::string JoinStringList(const StringList& strlist, const std::string& sep)
{
    std::string result;
    //debug("join(%d, '%hs')\n", strlist.size(), sep.c_str());
    for (StringList::const_iterator i=strlist.begin() ; i!=strlist.end() ; ++i)
    {
        if (!result.empty())
            result += sep;
        result += *i;
        //debug("  added %hs\n", (*i).c_str());
    }
    return result;
}

// sprintf like string formatting
std::string stringformat(char *fmt, ...)
{
    va_list ap;

#ifdef _WIN32_WCE
// unfortunately in CE there is now way of determining the resulting
// length of a formatted string.

    int desired_length= 1024;
#else
    // or use _scprintf to calculate result length
    // on ANSI-C compliant platforms snprintf will always return the desired length
    va_start(ap, fmt);
    int desired_length= _vsnprintf(NULL, 0, fmt, ap);
    va_end(ap);
#endif

    std::string str; str.resize(desired_length);

    va_start(ap, fmt);
    int printedlength= _vsnprintf(stringptr(str), str.size(), fmt, ap);
    va_end(ap);

    // '-1' means the buffer was too small.
    if (printedlength!=-1)
        str.resize(printedlength);
    return str;
}

std::string tolower(const std::string& str)
{
    std::string lstr; 
    lstr.reserve(str.size());
    for (std::string::const_iterator i=str.begin() ; i!=str.end() ; ++i)
        lstr += tolower(*i);
    return lstr;
}
int stringicompare(const std::string& a, const std::string& b)
{
    // todo: make this platform independent
    return _stricmp(a.c_str(), b.c_str());
}

//------------------------------------------------------
//#define GETBYTE(p,o)  (((BYTE*)(p))[o])
//#define GETWORD(p,o)  (GETBYTE(p,o)|GETBYTE(p,o+1)<<8)
//#define GETDWORD(p,o)  (GETWORD(p,o)|GETWORD(p,o+2)<<16)

//----------------------------------------------------------------------------
// utility functions for 'hexdump'
void hexdumpbytes(std::string &str, const BYTE *buf, int nLength)
{
    str.reserve(str.size()+nLength*3);
    while(nLength--)
    {
        str+= stringformat(" %02x", *buf++);
    }
}
void hexdumpwords(std::string &str, const WORD *buf, int nLength)
{
    str.reserve(str.size()+nLength*5);
    while(nLength--)
    {
        str+= stringformat(" %04x", *buf++);
    }
}
void hexdumpdwords(std::string &str, const DWORD *buf, int nLength)
{
    str.reserve(str.size()+nLength*9);
    while(nLength--)
    {
        str+= stringformat(" %08x", *buf++);
    }
}
void dumpascii(std::string &str, const BYTE *buf, int nLength)
{
    while(nLength--)
    {
        BYTE c= *buf++;
        str += (c>=' ' && c<='~')?c:'.';
    }
}
void writespaces(std::string &str, int n)
{
    while(n--)
    {
        str += ' ';
    }
}

//----------------------------------------------------------------------------
// various ways of generating a hexdump of binary data.

// dumps bytes, shorts, longs from a bytevector.
// in one long line, without offsets printed
std::string hexdump(const ByteVector& buf, int nDumpUnitSize /*=1*/)
{
    return hexdump(vectorptr(buf), buf.size(), nDumpUnitSize);
}

// dumps bytes, shorts or longs from a BYTE ptr + length, in one long line.
std::string hexdump(const BYTE *buf, int nLength, int nDumpUnitSize /*=1*/)
{
    int nCharsInResult= nLength*(nDumpUnitSize==1?3:
                         nDumpUnitSize==2?5:
                         nDumpUnitSize==4?9:9);
    std::string line;

    line.reserve(nCharsInResult);

    switch(nDumpUnitSize)
    {
        case 1: hexdumpbytes(line, buf, nLength); break;
        case 2: hexdumpwords(line, (WORD*)buf, nLength); break;
        case 4: hexdumpdwords(line, (DWORD*)buf, nLength); break;
    }
    return line;
}

// dumps data with a limited nr of items per line, followed by ascii data, prefixed with offsets.
std::string hexdump(DWORD dwOffset, const BYTE *buf, int nLength, int nDumpUnitSize /*=1*/, int nMaxUnitsPerLine /*=16*/)
{
    int nCharsInLine= 10+nMaxUnitsPerLine*(nDumpUnitSize==1?4:nDumpUnitSize==2?6:nDumpUnitSize==4?10:10);
    int nCharsInResult= nCharsInLine*(nLength/nDumpUnitSize/nMaxUnitsPerLine+1);

    std::string all; all.reserve(nCharsInResult);

    while(nLength>0)
    {
        std::string line;
        // is rounding correct here?
        int nUnitsInLine= nLength/nDumpUnitSize;
        if (nMaxUnitsPerLine<nUnitsInLine)
            nUnitsInLine= nMaxUnitsPerLine;
        
        line.reserve(nCharsInLine);

        line += stringformat("%08x", dwOffset);

        switch(nDumpUnitSize)
        {
            case 1: hexdumpbytes(line, buf, nUnitsInLine); break;
            case 2: hexdumpwords(line, (WORD*)buf, nUnitsInLine); break;
            case 4: hexdumpdwords(line, (DWORD*)buf, nUnitsInLine); break;
        }

        if (nUnitsInLine<nMaxUnitsPerLine)
            writespaces(line, (nMaxUnitsPerLine-nUnitsInLine)*(2*nDumpUnitSize+1));

        line += "  ";

        dumpascii(line, buf, nUnitsInLine*nDumpUnitSize);
        if (nUnitsInLine<nMaxUnitsPerLine)
            writespaces(line, nMaxUnitsPerLine-nUnitsInLine);

        all += line;
        all += "\n";

        nLength -= nUnitsInLine*nDumpUnitSize;
        dwOffset += nUnitsInLine*nDumpUnitSize;
        buf += nUnitsInLine*nDumpUnitSize;
    }

    return all;
}
